/*	Draggable
	Draggable allows you to add dragging capabilities to any element. Supports mobile devices too.
================================================== */
import { TLClass } from "../core/TLClass"
import Events from "../core/Events"
import { touch as BROWSER_TOUCH } from "../core/Browser"
import { mergeData, classMixin } from "../core/Util"
import { getPosition } from "../dom/DOM"
import { DOMEvent } from "../dom/DOMEvent"
import { Animate } from "../animation/Animate"
import { easeInOutQuint, easeOutStrong } from "../animation/Ease"

export class Draggable{

    constructor(drag_elem, options, move_elem) {
        // DOM ELements
        this._el = {
            drag: drag_elem,
            move: drag_elem
        }

        this.mousedrag = {
            down: "mousedown",
            up: "mouseup",
            leave: "mouseleave",
            move: "mousemove"
        }

        this.touchdrag = {
            down: "touchstart",
            up: "touchend",
            leave: "mouseleave",
            move: "touchmove"
        }

        if (move_elem) {
            this._el.move = move_elem;
        }

        //Options
        this.options = {
            enable: {
                x: true,
                y: true
            },
            constraint: {
                top: false,
                bottom: false,
                left: false,
                right: false
            },
            momentum_multiplier: 2000,
            duration: 1000,
            ease: easeInOutQuint
        };

        // Animation Object
        this.animator = null;

        // Drag Event Type
        this.dragevent = this.mousedrag;

        if (BROWSER_TOUCH) {
            this.dragevent = this.touchdrag;
        }

        // Draggable Data
        this.data = {
            sliding: false,
            direction: "none",
            pagex: {
                start: 0,
                end: 0
            },
            pagey: {
                start: 0,
                end: 0
            },
            pos: {
                start: {
                    x: 0,
                    y: 0
                },
                end: {
                    x: 0,
                    y: 0
                }
            },
            new_pos: {
                x: 0,
                y: 0
            },
            new_pos_parent: {
                x: 0,
                y: 0
            },
            time: {
                start: 0,
                end: 0
            },
            touch: false
        };

        // Merge Data and Options
        mergeData(this.options, options);
    }

    enable(e) {
        this.data.pos.start = 0;
        this._el.move.style.left = this.data.pos.start.x + "px";
        this._el.move.style.top = this.data.pos.start.y + "px";
        this._el.move.style.position = "absolute";
    }

    disable() {
        DOMEvent.removeListener(
            this._el.drag,
            this.dragevent.down,
            this._onDragStart,
            this
        );
        DOMEvent.removeListener(
            this._el.drag,
            this.dragevent.up,
            this._onDragEnd,
            this
        );
    }

    stopMomentum() {
        if (this.animator) {
            this.animator.stop();
        }
    }

    updateConstraint(c) {
        this.options.constraint = c;
    }

    /*	Private Methods
	================================================== */
    _onDragStart(e) {
        if (BROWSER_TOUCH) {
            if (e.originalEvent) {
                this.data.pagex.start = e.originalEvent.touches[0].screenX;
                this.data.pagey.start = e.originalEvent.touches[0].screenY;
            } else {
                this.data.pagex.start = e.targetTouches[0].screenX;
                this.data.pagey.start = e.targetTouches[0].screenY;
            }
        } else {
            this.data.pagex.start = e.pageX;
            this.data.pagey.start = e.pageY;
        }

        // Center element to finger or mouse
        if (this.options.enable.x) {
            this._el.move.style.left =
                this.data.pagex.start - this._el.move.offsetWidth / 2 + "px";
        }

        if (this.options.enable.y) {
            this._el.move.style.top =
                this.data.pagey.start - this._el.move.offsetHeight / 2 + "px";
        }

        this.data.pos.start = getPosition(this._el.drag);
        this.data.time.start = new Date().getTime();

        this.fire("dragstart", this.data);
        DOMEvent.addListener(
            this._el.drag,
            this.dragevent.move,
            this._onDragMove,
            this
        );
        DOMEvent.addListener(
            this._el.drag,
            this.dragevent.leave,
            this._onDragEnd,
            this
        );
    }

    _onDragEnd(e) {
        this.data.sliding = false;
        DOMEvent.removeListener(
            this._el.drag,
            this.dragevent.move,
            this._onDragMove,
            this
        );
        DOMEvent.removeListener(
            this._el.drag,
            this.dragevent.leave,
            this._onDragEnd,
            this
        );
        this.fire("dragend", this.data);

        //  momentum
        this._momentum();
    }

    _onDragMove(e) {
        e.preventDefault();
        this.data.sliding = true;

        if (BROWSER_TOUCH) {
            if (e.originalEvent) {
                this.data.pagex.end = e.originalEvent.touches[0].screenX;
                this.data.pagey.end = e.originalEvent.touches[0].screenY;
            } else {
                this.data.pagex.end = e.targetTouches[0].screenX;
                this.data.pagey.end = e.targetTouches[0].screenY;
            }
        } else {
            this.data.pagex.end = e.pageX;
            this.data.pagey.end = e.pageY;
        }

        this.data.pos.end = getPosition(this._el.drag);
        this.data.new_pos.x = -(
            this.data.pagex.start -
            this.data.pagex.end -
            this.data.pos.start.x
        );
        this.data.new_pos.y = -(
            this.data.pagey.start -
            this.data.pagey.end -
            this.data.pos.start.y
        );

        if (this.options.enable.x) {
            this._el.move.style.left = this.data.new_pos.x + "px";
        }

        if (this.options.enable.y) {
            this._el.move.style.top = this.data.new_pos.y + "px";
        }

        this.fire("dragmove", this.data);
    }

    _momentum() {
        var pos_adjust = {
                x: 0,
                y: 0,
                time: 0
            },
            pos_change = {
                x: 0,
                y: 0,
                time: 0
            },
            swipe = false,
            swipe_direction = "";

        if (BROWSER_TOUCH) {
            // Treat mobile multiplier differently
            //this.options.momentum_multiplier = this.options.momentum_multiplier * 2;
        }

        pos_adjust.time = (new Date().getTime() - this.data.time.start) * 10;
        pos_change.time = (new Date().getTime() - this.data.time.start) * 10;

        pos_change.x =
            this.options.momentum_multiplier *
            (Math.abs(this.data.pagex.end) - Math.abs(this.data.pagex.start));
        pos_change.y =
            this.options.momentum_multiplier *
            (Math.abs(this.data.pagey.end) - Math.abs(this.data.pagey.start));

        pos_adjust.x = Math.round(pos_change.x / pos_change.time);
        pos_adjust.y = Math.round(pos_change.y / pos_change.time);

        this.data.new_pos.x = Math.min(this.data.pos.end.x + pos_adjust.x);
        this.data.new_pos.y = Math.min(this.data.pos.end.y + pos_adjust.y);

        if (!this.options.enable.x) {
            this.data.new_pos.x = this.data.pos.start.x;
        } else if (this.data.new_pos.x < 0) {
            this.data.new_pos.x = 0;
        }

        if (!this.options.enable.y) {
            this.data.new_pos.y = this.data.pos.start.y;
        } else if (this.data.new_pos.y < 0) {
            this.data.new_pos.y = 0;
        }

        // Detect Swipe
        if (pos_change.time < 3000) {
            swipe = true;
        }

        // Detect Direction
        if (Math.abs(pos_change.x) > 10000) {
            this.data.direction = "left";
            if (pos_change.x > 0) {
                this.data.direction = "right";
            }
        }
        // Detect Swipe
        if (Math.abs(pos_change.y) > 10000) {
            this.data.direction = "up";
            if (pos_change.y > 0) {
                this.data.direction = "down";
            }
        }
        this._animateMomentum();
        if (swipe) {
            this.fire("swipe_" + this.data.direction, this.data);
        }
    }

    _animateMomentum() {
        var pos = {
                x: this.data.new_pos.x,
                y: this.data.new_pos.y
            },
            animate = {
                duration: this.options.duration,
                easing: easeOutStrong
            };

        if (this.options.enable.y) {
            if (this.options.constraint.top || this.options.constraint.bottom) {
                if (pos.y > this.options.constraint.bottom) {
                    pos.y = this.options.constraint.bottom;
                } else if (pos.y < this.options.constraint.top) {
                    pos.y = this.options.constraint.top;
                }
            }
            animate.top = Math.floor(pos.y) + "px";
        }

        if (this.options.enable.x) {
            if (this.options.constraint.left || this.options.constraint.right) {
                if (pos.x > this.options.constraint.left) {
                    pos.x = this.options.constraint.left;
                } else if (pos.x < this.options.constraint.right) {
                    pos.x = this.options.constraint.right;
                }
            }
            animate.left = Math.floor(pos.x) + "px";
        }

        this.animator = Animate(this._el.move, animate);

        this.fire("momentum", this.data);
    }
}

classMixin(Events)